#pragma once

#include "Core/Core.h"
#include "Core/Log.h"
#include <string>
#include <vector>


namespace Orange {

	enum class ShaderDataType {
		None = 0 , 
		Float,
		Float2,
		Float3,
		Float4,
		Mat3,
		Mat4,
		Int1,
		Int2,
		Int3,
		Int4,
		Bool,
	};
	
	static uint32_t ShaderTypeSize(ShaderDataType type) {
		switch (type)
		{
			case Orange::ShaderDataType::Float:  return 4;
			case Orange::ShaderDataType::Float2: return 4 * 2;
			case Orange::ShaderDataType::Float3: return 4 * 3;
			case Orange::ShaderDataType::Float4: return 4 * 4;
			case Orange::ShaderDataType::Mat3:	  return 4 * 3 * 3;
			case Orange::ShaderDataType::Mat4:	  return 4 * 4 * 4;
			case Orange::ShaderDataType::Int1:	  return 4 * 1;
			case Orange::ShaderDataType::Int2:	  return 4 * 2;
			case Orange::ShaderDataType::Int3:	  return 4 * 3;
			case Orange::ShaderDataType::Int4:	  return 4 * 4;
			case Orange::ShaderDataType::Bool:	  return 4;
		}
		ORANGE_CORE_ASSERT(false, "ShaderDataType unknown!"); 
		return 0;
	}

	struct BufferElement {
		uint32_t		m_offset;
		uint32_t		m_size;
		ShaderDataType  m_type;
		std::string		m_name;
		bool			m_normalized;

		BufferElement() {}
		BufferElement(ShaderDataType type,const std::string& name , bool normalize = false) :m_type(type), m_name(name),
			m_size(ShaderTypeSize(type)), m_offset(0), m_normalized(normalize){

		}
		uint32_t GetComponentCount(ShaderDataType type) {

			switch (type)
			{
				case Orange::ShaderDataType::Float:	return 1;
				case Orange::ShaderDataType::Float2:	return 2;
				case Orange::ShaderDataType::Float3:	return 3;
				case Orange::ShaderDataType::Float4:	return 4;
				case Orange::ShaderDataType::Mat3:		return 3 * 3;
				case Orange::ShaderDataType::Mat4:		return 4 * 4;
				case Orange::ShaderDataType::Int1:		return 1;
				case Orange::ShaderDataType::Int2:		return 2;
				case Orange::ShaderDataType::Int3:		return 3;
				case Orange::ShaderDataType::Int4:		return 4;
				case Orange::ShaderDataType::Bool:		return 1;
			}
			ORANGE_CORE_ASSERT(false, "ShaderDataType unknown!");
			return 0;
		}

	};
	/// <summary>
	/// ���沼�ַ�ʽ { 1.0f,1.0f,1.0f (����) ��1.0f,1.0f,1.0f, 1.0f ����ɫ�� }
	/// </summary>
	class BufferLayout {
	public:
		BufferLayout(){}
		BufferLayout(const std::initializer_list<BufferElement>& element) :m_elements(element) {
			/// ���� ����λ�õ� ƫ�� �� ����
			CalculateOffsetAndStride();
		}

		void CalculateOffsetAndStride() {
			uint32_t offset = 0; //ƫ��
			m_stride = 0; //����
			for (auto& element : m_elements) {
				element.m_offset = offset;
				offset += element.m_size;
				m_stride += element.m_size;
			}
		}
	 	 std::vector<BufferElement>::iterator begin() { return m_elements.begin(); };
		 std::vector<BufferElement>::iterator end() { return m_elements.end(); };
		inline std::vector<BufferElement> GetElements() const { return m_elements; };
		inline uint32_t GetStride() const { return m_stride; };
	private:
		std::vector<BufferElement> m_elements;
		uint32_t m_stride;
	};



	/// <summary>
	///  VertexBuffer ��ͳһ���������Ⱦapi ���õ� ���㻺�� �� ����Ĳ�ͬ�� ��Ⱦapi ʵ�ֶ��㻺�����ھ���Ķ��㻺��ʵ��
	/// </summary>
	class VertexBuffer
	{
	public:
		
		~VertexBuffer();

		virtual void SetData(void* data , uint32_t size) = 0;
		virtual void bind() const = 0;
		virtual void ubind() const = 0;
		virtual void setLayout( BufferLayout& layout ) = 0;
		virtual BufferLayout getLayout( ) const = 0;

		static VertexBuffer* Create(uint32_t size);
		static VertexBuffer* Create(float* vertexs, uint32_t size);
	};

	class IndexBuffer
	{
	public:

		~IndexBuffer();

		virtual void bind() const = 0;
		virtual void ubind() const = 0;
		virtual uint32_t getCount() const = 0;

		static IndexBuffer* Create(uint32_t* indexs, uint32_t count);
	};

}